package com.zlict.commonelasticsearch.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.http.client.methods.CloseableHttpResponse;

import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;

/**
 * Elasticsearch磁盘空间监控服务
 * 用于监控磁盘剩余空间，当空间不足时触发ILM策略调整
 * 
 * 优化说明：
 * - 移除了定时任务，改为按需触发
 * - 支持手动触发和API触发
 * - 基于业务配置的阈值进行磁盘监控
 */
@Service
@Slf4j
public class ElasticsearchDiskMonitorService {
    
    @Autowired
    private ElasticsearchILMService elasticsearchILMService;
    
    @Value("${elasticsearch.url:http://localhost:9200}")
    private String elasticsearchUrl;
    
    // 默认配置值
    private static final long DEFAULT_THRESHOLD_MB = 1024; // 默认1GB
    private static final int DEFAULT_RETENTION_MONTHS = 6; // 默认6个月
    
    /**
     * 智能检查磁盘空间并触发相应的ILM策略调整
     * 结合ES集群状态和磁盘空间进行综合判断
     * @return 是否触发了紧急模式
     */
    public boolean checkDiskSpaceAndTriggerILM() {
        return checkDiskSpaceAndTriggerILM(DEFAULT_THRESHOLD_MB);
    }
    
    /**
     * 智能检查磁盘空间并触发相应的ILM策略调整
     * 结合ES集群状态和磁盘空间进行综合判断
     * @param thresholdMB 阈值（MB）
     * @return 是否触发了紧急模式
     */
    public boolean checkDiskSpaceAndTriggerILM(long thresholdMB) {
        try {
            long thresholdBytes = thresholdMB * 1024 * 1024; // 转换为字节
            
            // 确定要检查的磁盘路径
            String checkPath = determineCheckPath();
            
            // 检查磁盘空间
            File file = new File(checkPath);
            long freeSpace = file.getUsableSpace();
            long totalSpace = file.getTotalSpace();
            long usedSpace = totalSpace - freeSpace;
            
            log.debug("磁盘空间检查 - 路径: {}, 总空间: {:.2f} GB, 已用: {:.2f} GB, 剩余: {:.2f} GB, 阈值: {:.2f} MB", 
                checkPath, 
                totalSpace / (1024.0 * 1024.0 * 1024.0),
                usedSpace / (1024.0 * 1024.0 * 1024.0),
                freeSpace / (1024.0 * 1024.0 * 1024.0),
                thresholdMB);
            
            // 检查是否低于阈值
            if (freeSpace < thresholdBytes) {
                log.warn("磁盘空间不足！剩余空间: {:.2f} MB, 阈值: {} MB, 路径: {}", 
                    freeSpace / (1024.0 * 1024.0), thresholdMB, checkPath);
                
                // 触发紧急ILM策略调整
                return triggerEmergencyILMStrategy();
            } else {
                log.debug("磁盘空间充足，剩余: {:.2f} MB", freeSpace / (1024.0 * 1024.0));
                return false;
            }
            
        } catch (Exception e) {
            log.error("检查磁盘空间失败", e);
            return false;
        }
    }
    
    /**
     * 基于ES集群状态的智能磁盘检查
     * 结合ES集群状态和实际磁盘空间进行综合判断
     * @return 是否触发了紧急模式
     */
    public boolean checkDiskSpaceWithESStatus() {
        return checkDiskSpaceWithESStatus(DEFAULT_THRESHOLD_MB);
    }
    
    /**
     * 基于ES集群状态的智能磁盘检查
     * 结合ES集群状态和实际磁盘空间进行综合判断
     * @param thresholdMB 阈值（MB）
     * @return 是否触发了紧急模式
     */
    public boolean checkDiskSpaceWithESStatus(long thresholdMB) {
        try {
            // 1. 检查ES集群磁盘使用情况
            String clusterDiskUsage = getElasticsearchClusterDiskUsage();
            if (clusterDiskUsage != null) {
                log.debug("ES集群磁盘使用情况: {}", clusterDiskUsage);
                
                // 解析ES集群磁盘使用情况，如果ES已经检测到磁盘空间不足
                // 则立即触发我们的ILM策略调整
                if (isESClusterDiskSpaceLow(clusterDiskUsage)) {
                    log.warn("ES集群检测到磁盘空间不足，触发紧急ILM策略");
                    return triggerEmergencyILMStrategy();
                }
            }
            
            // 2. 如果ES没有检测到问题，则进行我们的业务阈值检查
            return checkDiskSpaceAndTriggerILM(thresholdMB);
            
        } catch (Exception e) {
            log.error("基于ES状态的磁盘检查失败", e);
            // 降级到基础检查
            return checkDiskSpaceAndTriggerILM(thresholdMB);
        }
    }
    
    /**
     * 触发紧急ILM策略调整
     * 将所有温阶段的数据立即转为冷阶段
     */
    private boolean triggerEmergencyILMStrategy() {
        try {
            log.info("触发紧急ILM策略：将温阶段数据转为冷阶段");
            
            // 创建紧急ILM策略，将温阶段时间设为0（立即转为冷阶段）
            boolean success = elasticsearchILMService.createEmergencyILMPolicy();
            
            if (success) {
                log.info("紧急ILM策略创建成功，温阶段数据将立即转为冷阶段");
                
                // 应用紧急策略到所有审计日志索引
                elasticsearchILMService.applyEmergencyPolicyToAuditIndices();
                
                return true;
            } else {
                log.error("紧急ILM策略创建失败");
                return false;
            }
            
        } catch (Exception e) {
            log.error("触发紧急ILM策略失败", e);
            return false;
        }
    }
    
    /**
     * 恢复正常的ILM策略
     */
    public boolean restoreNormalILMStrategy() {
        return restoreNormalILMStrategy(DEFAULT_RETENTION_MONTHS);
    }
    
    /**
     * 恢复正常的ILM策略
     * @param retentionMonths 保留月数
     */
    public boolean restoreNormalILMStrategy(int retentionMonths) {
        try {
            log.info("恢复正常ILM策略");
            
            // 重新创建正常的ILM策略
            boolean success = elasticsearchILMService.createILMPolicy("audit_log_policy", retentionMonths);
            
            if (success) {
                log.info("正常ILM策略恢复成功");
                return true;
            } else {
                log.error("正常ILM策略恢复失败");
                return false;
            }
            
        } catch (Exception e) {
            log.error("恢复正常ILM策略失败", e);
            return false;
        }
    }
    
    
    /**
     * 获取当前磁盘空间信息
     */
    public DiskSpaceMonitorInfo getDiskSpaceInfo() {
        return getDiskSpaceInfo(DEFAULT_THRESHOLD_MB);
    }
    
    /**
     * 获取当前磁盘空间信息
     * @param thresholdMB 阈值（MB）
     */
    public DiskSpaceMonitorInfo getDiskSpaceInfo(long thresholdMB) {
        DiskSpaceMonitorInfo info = new DiskSpaceMonitorInfo();
        
        try {
            // 确定要检查的磁盘路径
            String checkPath = determineCheckPath();
            
            // 检查磁盘空间
            File file = new File(checkPath);
            long freeSpace = file.getUsableSpace();
            long totalSpace = file.getTotalSpace();
            long usedSpace = totalSpace - freeSpace;
            
            // 设置基本信息
            info.setCheckPath(checkPath);
            info.setTotalSpaceGB(totalSpace / (1024.0 * 1024.0 * 1024.0));
            info.setUsedSpaceGB(usedSpace / (1024.0 * 1024.0 * 1024.0));
            info.setFreeSpaceGB(freeSpace / (1024.0 * 1024.0 * 1024.0));
            info.setFreeSpaceMB(freeSpace / (1024.0 * 1024.0));
            info.setThresholdMB(thresholdMB);
            
            // 计算使用百分比
            double usedPercentage = totalSpace > 0 ? (usedSpace * 100.0 / totalSpace) : 0;
            info.setUsedPercentage(Math.round(usedPercentage * 100.0) / 100.0);
            
            // 判断是否告警
            boolean isAlert = freeSpace < (thresholdMB * 1024 * 1024);
            info.setIsAlert(isAlert);
            
            if (isAlert) {
                info.setAlertMessage(String.format("磁盘空间不足！剩余空间: %.2f MB, 阈值: %d MB", 
                    freeSpace / (1024.0 * 1024.0), thresholdMB));
            } else {
                info.setAlertMessage("磁盘空间充足");
            }
            
        } catch (Exception e) {
            log.error("获取磁盘空间信息失败", e);
            info.setAlertMessage("获取磁盘空间信息失败: " + e.getMessage());
            info.setIsAlert(true);
        }
        
        return info;
    }
    
    /**
     * 确定要检查的磁盘路径
     * 优先使用ES索引数据的真实磁盘路径
     */
    private String determineCheckPath() {
        try {
            // 首先尝试获取ES索引数据的真实磁盘路径
            String esDataPath = getElasticsearchDataPath();
            if (esDataPath != null && !esDataPath.trim().isEmpty()) {
                log.info("使用ES数据路径进行磁盘监控: {}", esDataPath);
                return esDataPath;
            }
            
            // 如果无法获取ES数据路径，使用项目运行目录
            String checkPath = System.getProperty("user.dir");
            log.info("使用默认磁盘检查路径: {}", checkPath);
            return checkPath;
            
        } catch (Exception e) {
            log.warn("确定磁盘检查路径失败，使用默认路径: {}", e.getMessage());
            return System.getProperty("user.dir");
        }
    }
    
    /**
     * 获取ES集群磁盘使用情况
     * @return 集群磁盘使用情况的JSON字符串
     */
    private String getElasticsearchClusterDiskUsage() {
        try {
            String url = elasticsearchUrl + "/_cat/allocation?v&h=node,disk.used_percent,disk.used,disk.total,disk.avail";
            
            try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                HttpGet request = new HttpGet(url);
                request.setHeader("Content-Type", "application/json");
                
                try (CloseableHttpResponse response = httpClient.execute(request)) {
                    if (response.getStatusLine().getStatusCode() == 200) {
                        return EntityUtils.toString(response.getEntity());
                    } else {
                        log.warn("获取ES集群磁盘使用情况失败，状态码: {}", response.getStatusLine().getStatusCode());
                        return null;
                    }
                }
            }
        } catch (Exception e) {
            log.error("获取ES集群磁盘使用情况失败", e);
            return null;
        }
    }
    
    /**
     * 判断ES集群磁盘空间是否不足
     * @param clusterDiskUsage ES集群磁盘使用情况
     * @return 是否磁盘空间不足
     */
    private boolean isESClusterDiskSpaceLow(String clusterDiskUsage) {
        try {
            // 解析ES集群磁盘使用情况
            // 格式示例：node1 85.2 45.2gb 53.1gb 7.9gb
            String[] lines = clusterDiskUsage.split("\n");
            
            for (String line : lines) {
                if (line.trim().isEmpty() || line.contains("node")) {
                    continue; // 跳过标题行
                }
                
                String[] parts = line.trim().split("\\s+");
                if (parts.length >= 2) {
                    try {
                        double diskUsedPercent = Double.parseDouble(parts[1]);
                        // 如果磁盘使用率超过85%，认为空间不足
                        if (diskUsedPercent > 85.0) {
                            log.warn("ES节点 {} 磁盘使用率过高: {:.1f}%", parts[0], diskUsedPercent);
                            return true;
                        }
                    } catch (NumberFormatException e) {
                        log.debug("解析磁盘使用率失败: {}", parts[1]);
                    }
                }
            }
            
            return false;
        } catch (Exception e) {
            log.error("解析ES集群磁盘使用情况失败", e);
            return false;
        }
    }
    
    /**
     * 获取Elasticsearch索引数据的真实磁盘路径
     */
    private String getElasticsearchDataPath() {
        try {
            // 通过ES API获取集群设置中的数据路径
            String url = elasticsearchUrl + "/_cluster/settings?include_defaults=true";
            HttpGet request = new HttpGet(url);
            request.setHeader("Content-Type", "application/json");
            
            try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                try (CloseableHttpResponse response = httpClient.execute(request)) {
                    if (response.getStatusLine().getStatusCode() == 200) {
                        String responseBody = EntityUtils.toString(response.getEntity());
                        
                        // 解析JSON响应，获取数据路径
                        ObjectMapper mapper = new ObjectMapper();
                        JsonNode rootNode = mapper.readTree(responseBody);
                        
                        // 查找数据路径设置
                        JsonNode defaults = rootNode.path("defaults");
                        JsonNode dataPath = defaults.path("path.data");
                        
                        if (dataPath.isArray() && dataPath.size() > 0) {
                            String path = dataPath.get(0).asText();
                            log.info("ES数据路径(默认设置): {}", path);
                            return path;
                        }
                        
                        // 如果默认设置中没有，尝试从持久化设置中获取
                        JsonNode persistent = rootNode.path("persistent");
                        dataPath = persistent.path("path.data");
                        if (dataPath.isArray() && dataPath.size() > 0) {
                            String path = dataPath.get(0).asText();
                            log.info("ES数据路径(持久化设置): {}", path);
                            return path;
                        }
                        
                        // 如果持久化设置中也没有，尝试从瞬态设置中获取
                        JsonNode transientSettings = rootNode.path("transient");
                        dataPath = transientSettings.path("path.data");
                        if (dataPath.isArray() && dataPath.size() > 0) {
                            String path = dataPath.get(0).asText();
                            log.info("ES数据路径(瞬态设置): {}", path);
                            return path;
                        }
                    }
                }
            }
            
            // 如果API调用失败，尝试从ES配置文件中获取
            return getElasticsearchDataPathFromConfig();
            
        } catch (Exception e) {
            log.warn("通过ES API获取数据路径失败: {}", e.getMessage());
            return getElasticsearchDataPathFromConfig();
        }
    }
    
    /**
     * 从ES配置文件中获取数据路径
     */
    private String getElasticsearchDataPathFromConfig() {
        try {
            // 尝试从常见的ES配置路径获取
            String[] possiblePaths = {
                "/etc/elasticsearch/elasticsearch.yml",
                "/usr/share/elasticsearch/config/elasticsearch.yml",
                System.getProperty("user.dir") + "/config/elasticsearch.yml",
                System.getProperty("user.home") + "/elasticsearch/config/elasticsearch.yml"
            };
            
            for (String configPath : possiblePaths) {
                File configFile = new File(configPath);
                if (configFile.exists()) {
                    String dataPath = parseDataPathFromConfig(configFile);
                    if (dataPath != null && !dataPath.isEmpty()) {
                        log.info("从配置文件获取ES数据路径: {} (配置文件: {})", dataPath, configPath);
                        return dataPath;
                    }
                }
            }
            
            // 如果都找不到，返回null，让调用方使用默认路径
            log.warn("无法从ES配置文件获取数据路径");
            return null;
            
        } catch (Exception e) {
            log.warn("从配置文件获取ES数据路径失败: {}", e.getMessage());
            return null;
        }
    }
    
    /**
     * 从ES配置文件中解析数据路径
     */
    private String parseDataPathFromConfig(File configFile) {
        try (BufferedReader reader = new BufferedReader(new FileReader(configFile))) {
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                // 跳过注释行
                if (line.startsWith("#")) {
                    continue;
                }
                if (line.startsWith("path.data:")) {
                    String dataPath = line.substring("path.data:".length()).trim();
                    // 移除引号
                    if (dataPath.startsWith("\"") && dataPath.endsWith("\"")) {
                        dataPath = dataPath.substring(1, dataPath.length() - 1);
                    } else if (dataPath.startsWith("'") && dataPath.endsWith("'")) {
                        dataPath = dataPath.substring(1, dataPath.length() - 1);
                    }
                    return dataPath;
                }
            }
        } catch (Exception e) {
            log.warn("解析ES配置文件失败: {}", e.getMessage());
        }
        return null;
    }
    
    /**
     * 磁盘空间监控信息
     */
    public static class DiskSpaceMonitorInfo {
        private String checkPath;
        private Double totalSpaceGB;
        private Double usedSpaceGB;
        private Double freeSpaceGB;
        private Double freeSpaceMB;
        private Double usedPercentage;
        private Long thresholdMB;
        private Boolean isAlert;
        private String alertMessage;
        
        // Getters and Setters
        public String getCheckPath() { return checkPath; }
        public void setCheckPath(String checkPath) { this.checkPath = checkPath; }
        
        public Double getTotalSpaceGB() { return totalSpaceGB; }
        public void setTotalSpaceGB(Double totalSpaceGB) { this.totalSpaceGB = totalSpaceGB; }
        
        public Double getUsedSpaceGB() { return usedSpaceGB; }
        public void setUsedSpaceGB(Double usedSpaceGB) { this.usedSpaceGB = usedSpaceGB; }
        
        public Double getFreeSpaceGB() { return freeSpaceGB; }
        public void setFreeSpaceGB(Double freeSpaceGB) { this.freeSpaceGB = freeSpaceGB; }
        
        public Double getFreeSpaceMB() { return freeSpaceMB; }
        public void setFreeSpaceMB(Double freeSpaceMB) { this.freeSpaceMB = freeSpaceMB; }
        
        public Double getUsedPercentage() { return usedPercentage; }
        public void setUsedPercentage(Double usedPercentage) { this.usedPercentage = usedPercentage; }
        
        public Long getThresholdMB() { return thresholdMB; }
        public void setThresholdMB(Long thresholdMB) { this.thresholdMB = thresholdMB; }
        
        public Boolean getIsAlert() { return isAlert; }
        public void setIsAlert(Boolean isAlert) { this.isAlert = isAlert; }
        
        public String getAlertMessage() { return alertMessage; }
        public void setAlertMessage(String alertMessage) { this.alertMessage = alertMessage; }
    }
}



